package uml2Java;

import java.util.Map;

import org.eclipse.emf.common.util.EList;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Enumeration;
import org.eclipse.uml2.uml.EnumerationLiteral;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.VisibilityKind;


import astjava.BodyDeclaration;
import astjava.ClassDeclaration;
import astjava.EnumConstantDeclaration;
import astjava.EnumDeclaration;
import astjava.FieldDeclaration;
import astjava.InterfaceDeclaration;
import astjava.JavaFactory;
import astjava.MethodDeclaration;
import astjava.Modifier;
import astjava.SingleVariableDeclaration;

public class TransformAttributes {
	
	private Package _umlPackage; 
	
	private Map<String, astjava.Type> _symbolMap;
	
	private TransformPackagesAndTypes _transformerTypes;

	public TransformAttributes(TransformPackagesAndTypes transformerTypes) {
		_umlPackage = transformerTypes.get_umlPackage();
		_symbolMap = transformerTypes.get_symbolMap();
		_transformerTypes = transformerTypes;
	}
	
	public void doTransformAttributes(){
		EList<Type> umlTypes = _umlPackage.getOwnedTypes();
		for (Type umlType : umlTypes) {
			if (umlType instanceof Enumeration) {
				Enumeration umlEnumeration = (Enumeration) umlType;
				EnumDeclaration javaEnum = (EnumDeclaration) _symbolMap.get(umlEnumeration.getName());
				for (EnumerationLiteral umlLiteral : umlEnumeration.getOwnedLiterals()) {
					javaEnum.getEnumConstants().add(transformLiteral(umlLiteral));
				}
			}
			if (umlType instanceof Interface) {
				Interface umlInterface = (Interface) umlType;
				InterfaceDeclaration javaInterface = (InterfaceDeclaration) _symbolMap.get(umlInterface.getName());
				for (Property umlAttribute : umlInterface.getOwnedAttributes()) {
					try {
						validateAttributeInInterface(umlAttribute);
						javaInterface.getBodyDeclarations().add(transformAttribute(umlAttribute));
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
			if (umlType instanceof Class) {
				Class umlClass = (Class) umlType;
				ClassDeclaration javaClass = (ClassDeclaration) _symbolMap.get(umlClass.getName());
				for (Property umlAttribute : umlClass.getOwnedAttributes()) {
					javaClass.getBodyDeclarations().add(transformAttribute(umlAttribute));
					if (!umlAttribute.isStatic() && umlAttribute.getVisibility().equals(VisibilityKind.PRIVATE_LITERAL)) {
						javaClass.getBodyDeclarations().add(generateGetter(umlAttribute));
						javaClass.getBodyDeclarations().add(generateSetter(umlAttribute));
					}
				}
			}
		}
	}
	
	private MethodDeclaration generateGetter(Property umlProperty) {
		MethodDeclaration getter = JavaFactory.eINSTANCE.createMethodDeclaration();
		getter.setName("get_"+umlProperty.getName());
		getter.setReturnType(JavaFactory.eINSTANCE.createTypeAccess());
		getter.getReturnType().setType(_symbolMap.get(umlProperty.getType().getName()));
		setPublicModifier(getter);
		return getter;
	}

	private void setPublicModifier(MethodDeclaration method) {
		Modifier modifier = JavaFactory.eINSTANCE.createModifier();
		modifier.setVisibility(astjava.VisibilityKind.PUBLIC);
		method.setModifier(modifier);
	}
	
	

	private BodyDeclaration generateSetter(Property umlProperty) {
		MethodDeclaration setter = JavaFactory.eINSTANCE.createMethodDeclaration();
		setter.setName("set_"+umlProperty.getName());
		setter.setReturnType(JavaFactory.eINSTANCE.createTypeAccess());
		setter.getReturnType().setType(_symbolMap.get("EVoid"));
		SingleVariableDeclaration parametre = JavaFactory.eINSTANCE.createSingleVariableDeclaration();
		parametre.setName(umlProperty.getName());
		parametre.setType(JavaFactory.eINSTANCE.createTypeAccess());
		parametre.getType().setType(_symbolMap.get(umlProperty.getType().getName()));
		setPublicModifier(setter);
		setter.getParameters().add(parametre);
		return setter;
	}

	private void validateAttributeInInterface(Property attribut) throws NonStaticVariableException, IllegalVisibityException {
		if (!attribut.isStatic()) {
			throw new NonStaticVariableException(attribut);
		}
		if (attribut.getVisibility().equals(VisibilityKind.PRIVATE_LITERAL) ||
				attribut.getVisibility().equals(VisibilityKind.PROTECTED_LITERAL)) {
			throw new IllegalVisibityException(attribut);
		}
	}
	
	private FieldDeclaration transformAttribute(Property attribut) {
		astjava.FieldDeclaration attributJava = JavaFactory.eINSTANCE.createFieldDeclaration();
		attributJava.setName(attribut.getName());
		attributJava.setModifier(JavaFactory.eINSTANCE.createModifier());
		attributJava.getModifier().setVisibility(_transformerTypes.transformVisibility(attribut.getVisibility()));
		attributJava.getModifier().setStatic(attribut.isStatic());
		attributJava.setType(JavaFactory.eINSTANCE.createTypeAccess());
		attributJava.getType().setType(transformType(attribut.getType()));
		return attributJava;
	}

	private astjava.Type transformType(Type type) {
		return _symbolMap.get(type.getName());
	}

	private EnumConstantDeclaration transformLiteral(EnumerationLiteral umlLiteral) {
		EnumConstantDeclaration javaLiteral = JavaFactory.eINSTANCE.createEnumConstantDeclaration();
		javaLiteral.setName(umlLiteral.getName());
		return javaLiteral;
	}
}
